package com.andy.qi.service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.andy.qi.conf.RoomServerConf;
import com.andy.qi.domain.Room;
import com.andy.qi.domain.ServerInfo;
import com.andy.qi.domain.User;
import com.andy.qi.repository.RoomRepository;
import com.andy.qi.repository.UserRepository;
import com.andy.qi.util.Http;
import com.andy.qi.util.Json;
import com.andy.qi.util.StrUtils;
import com.fasterxml.jackson.databind.JsonNode;

@Service
public class RoomServiceImpl implements RoomService {
    private Map<String, ServerInfo> serverMap = new ConcurrentHashMap<>();
    
    @Autowired private UserRepository userRepository;
    @Autowired private RoomRepository roomRepository;
    @Autowired private RoomServerConf config;
//    @Autowired private RestTemplate restTemplate;

    private ServerInfo chooseServer(){
        ServerInfo serverinfo = null;
        for(String s : serverMap.keySet()){
            ServerInfo info = serverMap.get(s);
            if(serverinfo == null){
                serverinfo = info;          
            } else {
                if(serverinfo.load > info.load){
                    serverinfo = info;
                }
            }
        }
        return serverinfo;
    }
    
    @Override
    public String createRoom(String account, String userId, String roomConf) {
        ServerInfo serverinfo = chooseServer();
        if(serverinfo == null){
            return null;
        }
        
        User user = userRepository.findOneByAccount(account);
        if(user == null) {
            return null;
        }
        //2、请求创建房间
        Map<String, String> params = new HashMap<>();
        params.put("userid", userId);
        params.put("gems", String.valueOf(user.getGems()));
        params.put("conf", StrUtils.encodeURL(roomConf));
        params.put("sign", StrUtils.md5(userId + roomConf + user.getGems() + config.getRoomPriKey()));
        
        String retStr = Http.get("http://" + serverinfo.ip + ":" + serverinfo.httpPort + "/create_room", params);
        if(StringUtils.isEmpty(retStr)) {
            return null;
        }
        System.out.println(retStr);
        JsonNode ret = Json.toJsonNode(retStr);
        if(ret.get("errcode").asInt() != 0) {
            return null;
        }
        return ret.get("roomid").asText();
    }

    @Override
    public EnterInfo enterRoom(String userId, String name, String roomId) {
        Map<String, String> params = new HashMap<>();
        params.put("userid", userId);
        params.put("name", name);
        params.put("roomid", roomId);
        params.put("sign", StrUtils.md5(userId + name + roomId + config.getRoomPriKey()));

        
        Room room = roomRepository.findOneById(roomId);
        if(room == null) {
            return null;
        }
        
        String id = room.ip + ":" + room.port;
        ServerInfo serverinfo = serverMap.get(id);
        if(serverinfo != null) {
            if(checkRoomIsRuning(serverinfo, roomId)){
                return enterRoomReq(serverinfo, userId, roomId, params);
            } else {
                return chooseServerAndEnter(serverinfo, userId, roomId, params);
            }
        } else {
            return chooseServerAndEnter(serverinfo, userId, roomId, params);
        }
    }
    
    private boolean checkRoomIsRuning(ServerInfo serverinfo, String roomId){
        String sign = StrUtils.md5(roomId + config.getRoomPriKey());
        Map<String, String> params = new HashMap<>();
        params.put("roomid", roomId);
        params.put("sign", sign);
        String retStr = Http.get("http://" + serverinfo.ip + ":" + serverinfo.httpPort + "/is_room_runing", params);
        if(StringUtils.isEmpty(retStr)) {
            return false;
        }
        JsonNode ret = Json.toJsonNode(retStr);
        return ret.get("errcode").asInt() == 0 && ret.get("runing").asBoolean();
    }

    private EnterInfo enterRoomReq(ServerInfo serverinfo, String userId, String roomId, Map<String, String> params){
        String retStr = Http.get("http://" + serverinfo.ip + ":" + serverinfo.httpPort + "/enter_room", params);
        if(StringUtils.isEmpty(retStr)) {
            return null;
        }
        JsonNode ret = Json.toJsonNode(retStr);
        if(ret.get("errcode").asInt() == 0) {
            User user = userRepository.findOne(userId);
            user.setRoomid(roomId);
            userRepository.save(user);
            EnterInfo enterInfo = new EnterInfo();
            enterInfo.setRoomid(roomId);
            enterInfo.setIp(serverinfo.clientip);
            enterInfo.setPort(serverinfo.clientport);
            enterInfo.setToken(ret.get("token").asText());
            return enterInfo;
        }
        return null;
    }

    private EnterInfo chooseServerAndEnter(ServerInfo serverinfo, String userId, String roomId, Map<String, String> params){
        serverinfo = chooseServer();
        if(serverinfo != null){
            return enterRoomReq(serverinfo, userId, roomId, params);
        } else{
            return null;                 
        }
    }

    @Override
    public boolean isServerOnline(String ip, int port) {
        String id = ip + ":" + port;
        ServerInfo serverInfo = serverMap.get(id);
        if(serverInfo == null){
            return false;
        }
        
        String sign = StrUtils.md5(config.getRoomPriKey());
        Map<String, String> params = new HashMap<>();
        params.put("sign", sign);
        
        String retStr = Http.get("http://" + serverInfo.ip + ":" + serverInfo.httpPort + "/ping", params);
        return !StringUtils.isEmpty(retStr);
    }

    @Override
    public boolean containsServer(String id) {
        return serverMap.containsKey(id);
    }

    @Override
    public ServerInfo getServer(String id) {
        return serverMap.get(id);
    }

    @Override
    public void setServer(String id, ServerInfo info) {
        serverMap.put(id, info);
    }
}
